home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Gold Collection / Software Vault - The Gold Collection (American Databankers) (1993).ISO / cdr49 / 121_01.zip / ML2.C < prev    next >
Text File  |  1993-06-01  |  21KB  |  807 lines

  1. /*
  2. HEADER: CUG 121.??;
  3.  
  4.     TITLE:    ml2 - modified l2 linker;
  5.     VERSION:    1.0;
  6.     DATE:    01/11/86;
  7.     DESCRIPTION: "ML2 is a version of Scott Layson's L2 linker which has been
  8.         modified to produce MP/M modules.  The only usage change is
  9.         the addition of the -r flag which will start the specified
  10.         runtime file at the specified address, then build the rest of
  11.         the module after it.
  12.         For further information on L2, see the reference below.";
  13.     KEYWORDS:    link;
  14.     SYSTEM:    MP/M;
  15.     FILENAME:    ML2.C;
  16.     WARNINGS:    "Requires chario.c for link (this is supplied with the
  17.         distributed version of l2).
  18.         This program has not been tested by the librarian, due to
  19.         lack of the MP/M operating system.";
  20.     AUTHORS:    Scott W. Layson, Stephen M. Kenton;
  21.     COMPILERS:    BDS-C 1.50;
  22.     REFERENCES:
  23.         AUTHORS: Scott W. Layson;
  24.         TITLE:   "The Mark of the Unicorn Linker for BDS C";
  25.     ENDREF
  26. */
  27.  
  28. /* ML2.C    New linker for BDS C to produce MP/M modules
  29.  
  30.         Written 1980 by Scott W. Layson
  31.         Modified from L2.C by Stephen M. Kenton
  32.         This code is in the public domain.
  33.  
  34. This is an improved linker for BDS C CRL format.  It eliminates the
  35. jump table at the beginning of each function in the object code,
  36. thus saving up to 10% or so in code space with a slight improvement
  37. in speed.  
  38.  
  39.         [81.4.13] Switches automatically to two-pass, windowing link
  40.                      if it runs out of memory.
  41.  
  42.         [81.4.13] Symbol table is now in the order that the
  43.                      functions occur in memory; this seems more
  44.                      useful.  Also, link stats are added on the end;
  45.                      this only slightly confuses SID.
  46.  
  47.         [81.4.27] "-wa" appends link stats to the .sym file; "-ws"
  48.                      writes them to a separate file; "-w" does
  49.                      neither.  "Version 2.1" message.
  50.  
  51.         [81.4.27] Overlay capability now exists.  It works a bit
  52.                      differently from CLINK:  the option
  53.                    -ovl <base name> <origin>
  54.                      causes symbols to be read from <base name>.sym
  55.                      and produces a segment linked at <origin>.  It
  56.                      also defaults the name of the overlay function to
  57.                      that of the first .crl file (to override, put -m
  58.                      after the -ovl), and replaces c.ccc with a jump
  59.                      to that function.  Also note that the base
  60.                      symbols are loaded at the very beginning, so name
  61.                      conflicts are always resolved in favor of the
  62.                      base.
  63.  
  64.         [82.1.1] "-R"  added to relocate the run time library.
  65.             The syntax is similar to the "-ovl" option,
  66.             but the file name is the run time library.
  67.  
  68. */
  69.  
  70.  
  71. /**************** Globals ****************/
  72.  
  73. #define    OVERLAYS
  74.  
  75. #include <bdscio.h>            /* for i/o buffer defs */
  76.  
  77. #define NUL        0
  78. #define FLAG        char
  79. #define repeat     while (1)
  80.  
  81. #define STDOUT        1
  82.  
  83. /* Phase control */
  84. #define INMEM        1        /* while everything still fits */
  85. #define DISK1        2        /* overflow; finish building table */
  86. #define DISK2        3        /* use table to do window link */
  87. int phase;
  88.  
  89.  
  90. /* function table */
  91. struct funct {
  92.     char fname[9];
  93.     FLAG flinkedp;            /* in memory already? */
  94.     char *faddr;            /* address of first ref link if not linked */
  95.     } ftab [300];
  96. int nfuncts;                /* no. of functions in  table */
  97.  
  98. #define LINKED        1        /* (flinkedp) function really here */
  99. #define EXTERNAL    2        /* function defined in separate symbol table */
  100.  
  101. char fdir [512];            /* CRL file function directory */
  102.  
  103. /* command line parameters etc. */
  104. int nprogs, nlibs;
  105. char progfiles [30] [15];    /* program file names */
  106. char libfiles [20] [15];        /* library file names */
  107. FLAG symsp,                /* write symbols to .sym file? */
  108.     appstatsp,            /* append stats to .sym file? */
  109.     sepstatsp;            /* write stats to .lnk file? */
  110. char mainfunct[10];
  111. #ifdef    OVERLAYS
  112. FLAG ovlp,                /* make overlay? */
  113.     relocp;                /* or relocate root? */
  114. #endif
  115. char runfile [15];            /* run time library file name */
  116. char symsfile [15];            /* file to load symbols from (for overlays) */
  117.  
  118. /* useful things to have defined */
  119. struct inst {
  120.     char opcode;
  121.     char *address;
  122.     };
  123.  
  124. union ptr {
  125.     unsigned u;            /* an int */
  126.     unsigned *w;            /* a word ptr */
  127.     char *b;                /* a byte ptr */
  128.     struct inst *i;        /* an instruction ptr */
  129.     };
  130.  
  131.  
  132. /* Link control variables */
  133.  
  134. union ptr codend;            /* last used byte of code buffer + 1 */
  135. union ptr exts;            /* start of externals */
  136. union ptr acodend;            /* actual code-end address */
  137. unsigned extspc;            /* size of externals */
  138. unsigned origin;            /* origin of code */
  139. unsigned buforg;            /* origin of code buffer */
  140. unsigned jtsaved;            /* bytes of jump table saved */
  141.  
  142. char *lspace;                /* space to link in */
  143. char *lspcend;                /* end of link area */
  144. char *lodstart;            /* beginning of current file */
  145.  
  146.  
  147. /* i/o buffer */
  148. struct iobuf {
  149.     int fd;
  150.     int isect;            /* currently buffered sector */
  151.     int nextc;            /* index of next char in buffer */
  152.     char buff [128];
  153.     } ibuf, obuf;
  154.  
  155. /* BDS C i/o buffer */
  156. char symbuf[BUFSIZ];
  157.  
  158. /* seek opcodes */
  159. #define ABSOLUTE 0
  160. #define RELATIVE 1
  161.  
  162. #define INPUT 0
  163.  
  164. #define TRUE (-1)
  165. #define FALSE 0
  166. #define NULL 0
  167.  
  168. /* 8080 instructions */
  169. #define LHLD 0x2A
  170. #define LXIH 0x21
  171. #define SPHL 0xF9
  172. #define JMP  0xC3
  173. #define CALL 0xCD
  174.  
  175. /* strcmp7 locals, made global for speed */
  176. char _c1, _c2, _end1, _end2;
  177.  
  178. /**************** End of Globals ****************/
  179.  
  180.  
  181.  
  182. main (argc, argv)
  183.     int argc;
  184.     char **argv;
  185. {
  186.     puts ("Mark of the Unicorn Linker for BDS C, vsn. 2.1M\n");
  187.     setup (argc, argv);
  188.     linkprog();
  189.     linklibs();
  190.     if (phase == DISK1) rescan();
  191.     else wrtcom();
  192.     if (symsp) wrtsyms();
  193.     }
  194.  
  195.  
  196. setup (argc, argv)            /* initialize function table, etc. */
  197.     int argc;
  198.     char **argv;
  199. {
  200.     symsp = appstatsp = sepstatsp = FALSE;
  201.     ovlp = relocp = FALSE;
  202.     nprogs = 0;
  203.     nlibs = 0;
  204.     strcpy (&mainfunct, "MAIN");
  205.     strcpy (&runfile, "C.CCC");
  206.     origin = 0x100;
  207.     cmdline (argc, argv);
  208.     lspace = endext();
  209.     lspcend = topofmem() - 1024;
  210.     loadccc();
  211.     nfuncts = 0;
  212. #ifdef OVERLAYS
  213.     if (ovlp) loadsyms();
  214. #endif
  215.     intern (&mainfunct);
  216.     phase = INMEM;
  217.     buforg = origin;
  218.     jtsaved = 0;
  219.     }
  220.  
  221.  
  222. cmdline (argc, argv)        /* process command line */
  223.     int argc;
  224.     char **argv;
  225. {
  226.     int i, progp;
  227.  
  228.     if (argc < 1) {
  229.         puts ("Usage is:\n");
  230.         puts ("  ml2 {program files} -l {library files} [-w]\n");
  231.         exit (1);
  232.         }
  233.     progp = TRUE;
  234.     for (i=1; i < argc; ++i) {
  235.         if (argv[i][0] == '-') {
  236.             if (!strcmp (argv[i], "-L")) progp = FALSE;
  237.             else if (!strcmp (argv[i], "-W")) symsp = TRUE;
  238.             else if (!strcmp (argv[i], "-WA")) symsp = appstatsp = TRUE;
  239.             else if (!strcmp (argv[i], "-WS")) symsp = sepstatsp = TRUE;
  240.             else if (!strcmp (argv[i], "-M")) {
  241.                 if (++i >= argc) Fatal ("-m argument missing.\n");
  242.                 strcpy (&mainfunct, argv[++i]);
  243.                 }
  244. #ifdef OVERLAYS
  245.             else if (!strcmp (argv[i], "-OVL")) {
  246.                 ovlp = TRUE;
  247.                 if (i + 2 >= argc) Fatal ("-ovl argument missing.\n");
  248.                 strcpy (&symsfile, argv[++i]);
  249.                 sscanf (argv[++i], "%x", &origin);
  250.                 }
  251.             else if (!strcmp (argv[i], "-R")) {
  252.                 relocp = TRUE;
  253.                 if (i + 2 >= argc) Fatal ("-r argument missing");
  254.                 strcpy (&runfile, argv[++i]);
  255.                 sscanf (argv[++i], "%x", &origin);
  256.                 }
  257. #endif
  258.             else printf ("Unknown option: '%s'\n", argv[i]);
  259.             }
  260.         else {
  261.             if (progp) strcpy (&progfiles[nprogs++], argv[i]);
  262.             else strcpy (&libfiles[nlibs++], argv[i]);
  263.             }
  264.         }
  265.     if (ovlp) strcpy (&mainfunct, &progfiles[0]);
  266.     strcpy(&libfiles[nlibs++], "LOCAL.CRL") ;
  267.     strcpy(&libfiles[nlibs++], "DEFF.CRL") ;
  268.     strcpy(&libfiles[nlibs++], "DEFF2.CLR") ;
  269.     }
  270.  
  271.  
  272. loadccc()                    /* load C.CCC (runtime library) */
  273. {
  274.     union ptr temp;
  275.     unsigned len;
  276.  
  277.     codend.b = lspace;
  278.     if (!ovlp) {
  279.         if (copen (&ibuf, runfile) < 0)
  280.             Fatal ("Can't open C.CCC\n");
  281.         if (cread (&ibuf, lspace, 128) < 128)    /* read a sector */
  282.             Fatal ("C.CCC: read error!\n");
  283.         temp.b = lspace + 0x17;
  284.         len = *temp.w;                        /* how long is it? */
  285.         cread (&ibuf, lspace + 128, len - 128);    /* read rest */
  286.         codend.b += len;
  287.         cclose (&ibuf);
  288.         }
  289.     else codend.i++->opcode = JMP;
  290.     }
  291.  
  292.  
  293. linkprog()                /* link in all program files */
  294. {
  295.     int i;
  296.     union ptr dirtmp;
  297.     struct funct *fnct;
  298.  
  299.     for (i=0; i<nprogs; ++i) {
  300.         makeext (&progfiles[i], "CRL");
  301.         if (copen (&ibuf, progfiles[i]) < 0) {
  302.             printf ("Can't open %s\n", progfiles[i]);
  303.             continue;
  304.             }
  305.         printf ("\n<< Loading %s >>\n", &progfiles[i]);
  306.         readprog (i==0);
  307.         for (dirtmp.b=&fdir; *dirtmp.b != 0x80;) {
  308.             fnct = intern (dirtmp.b);            /* for each module */
  309.             skip7 (&dirtmp);                    /* in directory */
  310.             if (!fnct->flinkedp)
  311.                 linkmod (fnct, lodstart + *dirtmp.w - 0x205);
  312.             else if (phase != DISK2) {
  313.                 puts ("Duplicate program function '");
  314.                 puts (&fnct->fname);
  315.                 puts ("', not linked.\n");
  316.                 }
  317.             dirtmp.w++;
  318.             }                                /* intern & link it */
  319.         cclose (&ibuf);
  320.         }
  321.     }
  322.  
  323.  
  324. linklibs()                /* link in library files */
  325. {
  326.     int ifile;
  327.  
  328.     for (ifile=0; ifile<nlibs; ++ifile) scanlib (ifile);
  329.     while (missingp()) {
  330.         puts ("Enter the name of a file to be searched: ");
  331.         gets (&libfiles[nlibs]);
  332.         scanlib (nlibs++);
  333.         }
  334.     acodend.b = codend.b - lspace + buforg;        /* save that number! */
  335.     if (!exts.b) exts.b = acodend.b;
  336.     }
  337.  
  338.  
  339. missingp()                /* are any functions missing?  print them out */
  340. {
  341.     int i, foundp;
  342.  
  343.     foundp = FALSE;
  344.     for (i=0; i<nfuncts; ++i)
  345.         if (!ftab[i].flinkedp) {
  346.             if (!foundp) puts ("*** Missing functions:\n");
  347.             puts (&ftab[i].fname);
  348.             puts ("\n");
  349.             foundp = TRUE;
  350.             }
  351.     return (foundp);
  352.     }
  353.  
  354.  
  355. rescan()                    /* perform second disk phase */
  356. {
  357.     int i;
  358.     
  359.     for (i=0; i < nfuncts; ++i)
  360.         if (ftab[i].flinkedp == LINKED) ftab[i].flinkedp = FALSE;
  361.     phase = DISK2;
  362.     buforg = origin;
  363.     puts ("\n\n**** Beginning second disk pass ****\n");
  364.     if (!ovlp) makeext (&progfiles[0], "COM");
  365.     else makeext (&progfiles[0], "OVL");
  366.     ccreat (&obuf, &progfiles[0]);
  367.     loadccc();
  368.     hackccc();
  369.     linkprog();
  370.     linklibs();
  371.     if (cwrite (&obuf, lspace, codend.b - lspace) == -1
  372.         ||  cflush (&obuf) < 0) Fatal ("Disk write error!\n");
  373.     cclose (&obuf);
  374.     stats (STDOUT);
  375.     }
  376.  
  377.  
  378.  
  379. readprog (mainp)            /* read in a program file */
  380.     FLAG mainp;
  381. {
  382.     char extp;                            /* was -e used? */
  383.     char *extstmp;
  384.     union ptr dir;
  385.     unsigned len;
  386.  
  387.     if (cread (&ibuf, &fdir, 512) < 512)            /* read directory */
  388.         Fatal ("-- read error!\n");
  389.     if (phase == INMEM  &&  mainp) {
  390.         cread (&ibuf, &extp, 1);
  391.         cread (&ibuf, &extstmp, 2);
  392.         cread (&ibuf, &extspc, 2);
  393.         if (extp) exts.b = extstmp;
  394.         else exts.b = 0;                        /* will be set later */
  395.         }
  396.     else cseek (&ibuf, 5, RELATIVE);
  397.     for (dir.b=&fdir; *dir.b != 0x80; nextd (&dir));    /* find end of dir */
  398.     ++dir.b;
  399.     len = *dir.w - 0x205;
  400.     readobj (len);
  401.     }
  402.  
  403.  
  404. readobj (len)                /* read in an object (program or lib funct) */
  405.     unsigned len;
  406. {
  407.     if (phase == DISK1  ||  codend.b + len >= lspcend) {
  408.         if (phase == INMEM) {
  409.             puts ("\n** Out of memory -- switching to disk mode **\n");
  410.             phase = DISK1;
  411.             }
  412.         if (phase == DISK2) {
  413.             if (cwrite (&obuf, lspace, codend.b - lspace) == -1)
  414.                 Fatal ("Disk write error!\n");
  415.             }
  416.         buforg += codend.b - lspace;
  417.         codend.b = lspace;
  418.         if (codend.b + len >= lspcend)
  419.             Fatal ("Module won't fit in memory at all!\n");
  420.         }
  421.     lodstart = codend.b;
  422.     if (cread (&ibuf, lodstart, len) < len) Fatal ("-- read error!\n");
  423.     }
  424.  
  425.  
  426. scanlib (ifile)
  427.     int ifile;
  428. {
  429.     int i;
  430.     union ptr dirtmp;
  431.  
  432.     makeext (&libfiles[ifile], "CRL");
  433.     if (copen (&ibuf, libfiles[ifile]) < 0) {
  434.         printf ("Can't open %s\n", libfiles[ifile]);
  435.         return;
  436.         }
  437.     printf ("\n<< Scanning %s >>\n", &libfiles[ifile]);
  438.     if (cread (&ibuf, &fdir, 512) < 512)    /* read directory */
  439.         Fatal ("-- Read error!\n");
  440.     for (i=0; i<nfuncts; ++i) {            /* scan needed functions */
  441.         if (!ftab[i].flinkedp
  442.             && (dirtmp.b = dirsearch (&ftab[i].fname))) {
  443.             readfunct (dirtmp.b);
  444.             linkmod (&ftab[i], lodstart);
  445.             }
  446.         }
  447.     cclose (&ibuf);
  448.     }
  449.  
  450.  
  451. readfunct (direntry)            /* read a function (from a library) */
  452.     union ptr direntry;
  453. {
  454.     unsigned start, len;
  455.  
  456.     skip7 (&direntry);
  457.     start = *direntry.w++;
  458.     skip7 (&direntry);
  459.     len = *direntry.w - start;
  460.     if (cseek (&ibuf, start, ABSOLUTE) < 0) Fatal (" -- read error!");
  461.     readobj (len);
  462.     }
  463.  
  464.  
  465. linkmod (fnct, modstart)            /* link in a module */
  466.     struct funct *fnct;
  467.     union ptr    modstart;                    /* loc. of module in memory */
  468.  
  469. {
  470.     union ptr temp,
  471.             jump,                    /* jump table temp */
  472.             body,                    /* loc. of function in memory */
  473.             code,                    /* loc. of code proper in mem. */
  474.             finalloc;                    /* runtime loc. of function */
  475.     unsigned flen, nrelocs, jtsiz, offset;
  476.  
  477.     fnct->flinkedp = LINKED;
  478.     if (phase != DISK2) {
  479.         finalloc.b = codend.b - lspace + buforg;
  480.         if (phase == INMEM) chase (fnct->faddr, finalloc.b);
  481.         fnct->faddr = finalloc.b;
  482.         }
  483.      else finalloc.b = fnct->faddr;
  484.     body.b = modstart.b + strlen(modstart.b) + 3; /* loc. of function body */
  485.     jump.i = body.i + (*modstart.b ? 1 : 0);
  486.     for (temp.b = modstart.b; *temp.b; skip7(&temp)) {
  487.         jump.i->address = intern (temp.b);
  488.         ++jump.i;
  489.         }
  490.     ++temp.b;
  491.     flen = *temp.w;
  492.     code.b = jump.b;
  493.     temp.b = body.b + flen;                /* loc. of reloc parameters */
  494.     nrelocs = *temp.w++;
  495.     jtsiz = code.b - body.b;
  496.     offset = code.b - codend.b;
  497.     if (phase != DISK1)
  498.         while (nrelocs--) relocate (*temp.w++, body.b, jtsiz,
  499.                                finalloc.b, offset, flen);
  500.     flen -= jtsiz;
  501.     if (phase != DISK2) jtsaved += jtsiz;
  502.     if (phase != DISK1) movmem (code.b, codend.b, flen);
  503.     codend.b += flen;
  504.     }
  505.  
  506.  
  507. relocate (param, body, jtsiz, base, offset, flen)    /* do a relocation!! */
  508.     unsigned param, jtsiz, base, offset, flen;
  509.     union ptr body;
  510. {
  511.     union ptr instr,                    /* instruction involved */
  512.             ref;                        /* jump table link */
  513.     struct funct *fnct;
  514.  
  515.     if (param == 1) return;                /* don't reloc jt skip */
  516.     instr.b = body.b + param - 1;
  517.     if (instr.i->address >= jtsiz)
  518.         instr.i->address += base - jtsiz;            /* vanilla case */
  519.     else {
  520.         ref.b = instr.i->address + body.u;
  521.         if (instr.i->opcode == LHLD) {
  522.             instr.i->opcode = LXIH;
  523.             --ref.b;
  524.             }
  525.         fnct = ref.i->address;
  526.         instr.i->address = fnct->faddr;        /* link in */
  527.         if (!fnct->flinkedp  &&  phase == INMEM)
  528.             fnct->faddr = instr.b + 1 - offset;    /* new list head */
  529.         }
  530.     }
  531.  
  532.  
  533. intern (name)                /* intern a function name in the table */
  534.     char *name;
  535. {
  536.     struct funct *fptr;
  537.  
  538.     if (*name == 0x9D) name = "MAIN";        /* Why, Leor, WHY??? */
  539.     for (fptr = &ftab[nfuncts-1]; fptr >= ftab; --fptr) 
  540.         if (!strcmp7 (name, fptr->fname)) break;
  541.     if (fptr < ftab) {
  542.         fptr = &ftab[nfuncts];
  543.         strcpy7 (fptr->fname, name);
  544.         str7tont (fptr->fname);
  545.         fptr->flinkedp = FALSE;
  546.         fptr->faddr = NULL;
  547.         ++nfuncts;
  548.         }
  549.     return (fptr);
  550.     }
  551.  
  552.  
  553. dirsearch (name)            /* search directory for a function */
  554.     char *name;
  555. {
  556.     union ptr temp;
  557.  
  558.     for (temp.b = &fdir; *temp.b != 0x80; nextd (&temp))
  559.         if (!strcmp7 (name, temp.b)) return (temp.b);
  560.     return (NULL);
  561.     }
  562.  
  563.  
  564. nextd (ptrp)                /* move this pointer to the next dir entry */
  565.     union ptr *ptrp;
  566. {
  567.     skip7 (ptrp);
  568.     ++(*ptrp).w;
  569.     }
  570.  
  571.  
  572. chase (head, loc)            /* chase chain of refs to function */
  573.     union ptr head;
  574.     unsigned loc;
  575. {
  576.     union ptr temp;
  577.  
  578.     while (head.w) {
  579.         temp.w = *head.w;
  580.         *head.w = loc;
  581.         head.u = temp.u;
  582.         }
  583.     }
  584.  
  585.  
  586. wrtcom()                    /* write out com file (from in-mem link) */
  587. {
  588.     hackccc();
  589.     if (!ovlp) makeext (&progfiles[0], "COM");
  590.     else makeext (&progfiles[0], "OVL");
  591.     if (!ccreat (&obuf, &progfiles[0]) < 0
  592.         ||  cwrite (&obuf, lspace, codend.b - lspace) == -1
  593.         ||  cflush (&obuf) < 0)
  594.         Fatal ("Disk write error!\n");
  595.     cclose (&obuf);
  596.     stats (STDOUT);
  597.     }
  598.  
  599.  
  600. hackccc()                    /* store various goodies in C.CCC code */
  601. {
  602.     union ptr temp;
  603.     struct funct *fptr;
  604.  
  605.     temp.b = lspace;
  606.     fptr = intern (&mainfunct);
  607.     if (!ovlp) {
  608.         temp.i->opcode = LHLD;
  609.         temp.i->address = origin + 6 - 0x100;
  610.         (++temp.i)->opcode = SPHL;
  611.         temp.b = lspace + 0xF;            /* main function address */
  612.         temp.i->address = fptr->faddr;
  613.         temp.b = lspace + 0x15;
  614.         *temp.w++ = exts.u;
  615.         ++temp.w;
  616.         *temp.w++ = acodend.u;
  617.         *temp.w++ = exts.u + extspc;
  618.         }
  619.     else temp.i->address = fptr->faddr;        /* that's a JMP */
  620.     }
  621.  
  622.  
  623. wrtsyms()                    /* write out symbol table */
  624. {
  625.     int i, fd, compar();
  626.     
  627.     qsort (&ftab, nfuncts, sizeof(*ftab), &compar);
  628.     makeext (&progfiles[0], "SYM");
  629.     if (fcreat (&progfiles[0], &symbuf) < 0)
  630.         Fatal ("Can't create .SYM file\n");
  631.     for (i=0; i < nfuncts; ++i) {
  632.         puthex (ftab[i].faddr, &symbuf);
  633.         putc (' ', &symbuf);
  634.         fputs (&ftab[i].fname, &symbuf);
  635.         if (i % 4 == 3) fputs ("\n", &symbuf);
  636.         else {
  637.             if (strlen (&ftab[i].fname) < 3) putc ('\t', &symbuf);
  638.             putc ('\t', &symbuf);
  639.             }
  640.         }
  641.     if (i % 4) fputs ("\n", &symbuf);    
  642.     if (appstatsp) stats (&symbuf);
  643.     putc (CPMEOF, &symbuf);
  644.     fflush (&symbuf);
  645.     fclose (&symbuf);
  646.     if (sepstatsp) {
  647.         makeext (&progfiles[0], "LNK");
  648.         if (fcreat (&progfiles[0], &symbuf) < 0)
  649.             Fatal ("Can't create .LNK file\n");
  650.         stats (&symbuf);
  651.         putc (CPMEOF, &symbuf);
  652.         fflush (&symbuf);
  653.         fclose (&symbuf);
  654.         }
  655.     }
  656.  
  657.  
  658. compar (f1, f2)            /* compare two symbol table entries by name */
  659.     struct funct *f1, *f2;
  660. {
  661. /*    return (strcmp (&f1->fname, &f2->fname));    alphabetical order */
  662.     return (f1->faddr > f2->faddr);            /* memory order */
  663.     }
  664.  
  665.  
  666. #ifdef OVERLAYS
  667. loadsyms()                /* load base symbol table (for overlay) */
  668. {                        /* symbol table must be empty! */
  669.     int nread;
  670.     FLAG done;
  671.     char *c;
  672.     
  673.     makeext (&symsfile, "SYM");
  674.     if (fopen (&symsfile, &symbuf) < 0) 
  675.         Fatal ("Can't open %s.\n", &symsfile);
  676.     done = FALSE;
  677.     while (!done) {
  678.         nread = fscanf (&symbuf, "%x%s\t%x%s\t%x%s\t%x%s\n",
  679.                      &(ftab[nfuncts].faddr), &(ftab[nfuncts].fname),
  680.                      &(ftab[nfuncts+1].faddr), &(ftab[nfuncts+1].fname),
  681.                      &(ftab[nfuncts+2].faddr), &(ftab[nfuncts+2].fname),
  682.                      &(ftab[nfuncts+3].faddr), &(ftab[nfuncts+3].fname));
  683.         nread /= 2;
  684.         if (nread < 4) done = TRUE;
  685.         while (nread-- > 0) ftab[nfuncts++].flinkedp = EXTERNAL;
  686.         }
  687.     fclose (&symbuf);
  688.     }
  689. #endif
  690.  
  691.  
  692. stats (chan)                /* print statistics on chan */
  693.     int chan;
  694. {
  695.     unsigned temp;
  696.  
  697.     fprintf (chan, "\n\nLink statistics:\n");
  698.     fprintf (chan, "  Number of functions: %d\n", nfuncts);
  699.     fprintf (chan, "  Code ends at: 0x%x\n", acodend.u);
  700.     fprintf (chan, "  Externals begin at: 0x%x\n", exts.u);
  701.     fprintf (chan, "  Externals end at: 0x%x\n", exts.u + extspc);
  702.     fprintf (chan, "  Jump table bytes saved: 0x%x\n", jtsaved);
  703.     temp = lspcend;
  704.     if (phase == INMEM)
  705.         fprintf (chan,
  706.                 "  Link space remaining: %dK\n", (temp - codend.u) / 1024);
  707.     }
  708.  
  709.  
  710. makeext (fname, ext)        /* force a file extension to ext */
  711.     char *fname, *ext;
  712. {
  713.     while (*fname && (*fname != '.')) {
  714.         *fname = toupper (*fname);        /* upcase as well */
  715.         ++fname;
  716.         }
  717.     *fname++ = '.';
  718.     strcpy (fname, ext);
  719.     }
  720.  
  721.  
  722. strcmp7 (s1, s2)            /* compare two bit-7-terminated strings */
  723.     char *s1, *s2;            /* also works for non-null NUL-term strings */
  724. {
  725. /*    char c1, c2, end1, end2;        (These are now global for speed) */
  726.  
  727.     repeat {
  728.          _c1 = *s1++;
  729.         _c2 = *s2++;
  730.         _end1 = (_c1 & 0x80) | !*s1;
  731.         _end2 = (_c2 & 0x80) | !*s2;
  732.         if ((_c1 &= 0x7F) < (_c2 &= 0x7F)) return (-1);
  733.         if (_c1 > _c2  ||  (_end2  &&  !_end1)) return (1);
  734.         if (_end1  &&  !_end2) return (-1);
  735.         if (_end1  &&  _end2) return (0);
  736.         }
  737.     }
  738.  
  739.  
  740. strcpy7 (s1, s2)            /* copy s2 into s1 */
  741.     char *s1, *s2;
  742. {
  743.     do {
  744.         *s1 = *s2;
  745.         if (!*(s2+1)) {                /* works even if */
  746.             *s1 |= 0x80;                /* s2 is null-term */
  747.             break;
  748.             }
  749.         ++s1;
  750.         } while (!(*s2++ & 0x80));
  751.     }
  752.  
  753.  
  754. skip7 (ptr7)                /* move this pointer past a string */
  755.     char **ptr7;
  756. {
  757.     while (!(*(*ptr7)++ & 0x80));
  758.     }
  759.  
  760.  
  761. str7tont (s)                /* add null at end */
  762.     char *s;
  763. {
  764.     while (!(*s & 0x80)) {
  765.         if (!*s) return;        /* already nul term! */
  766.         s++;
  767.         }
  768.     *s = *s & 0x7F;
  769.     *++s = NUL;
  770.     }
  771.  
  772.  
  773. puthex (n, obuf)            /* output a hex word, with leading 0s */
  774.     unsigned n;
  775.     char *obuf;
  776. {
  777.     int i, nyb;
  778.     
  779.     for (i = 3; i >= 0; --i) {
  780.         nyb = (n >> (i * 4)) & 0xF;
  781.         nyb += (nyb > 9) ? 'A' - 10 : '0';
  782.         putc (nyb, obuf);
  783.         }
  784.     }
  785.  
  786.  
  787. Fatal (arg1, arg2, arg3, arg4)    /* lose, lose */
  788.     char *arg1, *arg2, *arg3, *arg4;
  789. {
  790.     printf (arg1, arg2, arg3, arg4);
  791.     exit (1);
  792.     }
  793.  
  794.  
  795. exit (status)                /* exit the program */
  796.     int status;
  797. {
  798.     if (status == 1) {
  799.         unlink ("a:$$$.sub");
  800.         }
  801.     bdos(0,NULL);                    /* bye! */
  802.     }
  803.  
  804.  
  805.  
  806. /* END OF L2.C */
  807.